#-*- coding:utf-8 -*-
import psyco
psyco.full()

import os,sys,math
from PIL import Image
import SimpleFilter
from opencv import cv,adaptors
import numpy

import ctypes
libCCV=ctypes.CDLL(os.path.abspath(os.path.dirname(__file__))+"/ccv.so")
libHOG=ctypes.CDLL(os.path.abspath(os.path.dirname(__file__))+"/hog.so")
libGIST=ctypes.CDLL(os.path.abspath(os.path.dirname(__file__))+"/gist.so")


class FeatureVector:
    def __init__(self,image,maxsize=0):
        if maxsize==0:
            self.image=image
        else:
            self.image=SimpleFilter.Zoom(image,maxsize)


    def GetVector(self):
        if not hasattr(self,"_v"):
            self._calc()
        return self._v
    
    def _calc(self):
        raise NotImplementedError("implement _calc() which set the feature vector of self.image into self._v")


class CCV(FeatureVector):
    def __init__(self,image,th=4):
        FeatureVector.__init__(self,image)
        self.th=th
    
    class _imagePixT(ctypes.Structure):
        _fields_=[
                ("width",ctypes.c_int)
                ,("height",ctypes.c_int)
                ,("pixR",ctypes.POINTER(ctypes.c_int))
                ,("pixG",ctypes.POINTER(ctypes.c_int))
                ,("pixB",ctypes.POINTER(ctypes.c_int))
                ]
    def _createImagePixTFromNumPy(self,imgArr,width,height):
        cInts=ctypes.c_int*(width*height) 
        pixR=cInts()
        pixG=cInts()
        pixB=cInts()
        
        for y in xrange(height):
            for x in xrange(width):
                i=y*width+x
                pixR[i],pixG[i],pixB[i]=imgArr[y,x]
                        
        return self._imagePixT(width,height,pixR,pixG,pixB)
    
    def _calc(self):
        _img=adaptors.PIL2Ipl(self.image)
        width,height=self.image.size
        _img2=cv.cvCreateImage(cv.cvSize(width,height),cv.IPL_DEPTH_8U,3)
        cv.cvSmooth(_img,_img2,cv.CV_GAUSSIAN,5)
        imgArr=adaptors.Ipl2NumPy(_img2)
        del _img,_img2
        imgPix=self._createImagePixTFromNumPy(imgArr,width,height)
        del imgArr
        
        th=ctypes.c_int(self.th)
        cCalcCCV=libCCV.calcCCV
        cCalcCCV.restype=ctypes.POINTER(ctypes.c_int)
        
        desc=cCalcCCV(ctypes.byref(imgPix),th)
        
        self._v=[desc[i] for i in xrange(32)]
        del desc

class HOG(FeatureVector):
    def __init__(self,image,cellRow=4,cellCol=4,blockWidth=3,blockHeight=3,histgramDimension=9):
        FeatureVector.__init__(self,image)
        self.cellRow=cellRow
        self.cellCol=cellCol
        self.blockWidth=blockWidth
        self.blockHeight=blockWidth
        self.histgramDimension=histgramDimension
    
    class _imagePixT(ctypes.Structure):
        _fields_=[
                ("width",ctypes.c_int)
                ,("height",ctypes.c_int)
                ,("pix",ctypes.POINTER(ctypes.c_int))
                ]
    def _createImagePixTFromPIL(self,image):
        width,height=image.size
        cInts=ctypes.c_int*(width*height) 
        pix=cInts()
        _pix=image.load()
        for y in xrange(height):
            for x in xrange(width):
                i=y*width+x
                pix[i]=_pix[x,y]
                        
        return self._imagePixT(width,height,pix)

    def _calc(self):
        imgPix=self._createImagePixTFromPIL(self.image.convert("L"))
        
        cCalcHOG=libHOG.calcHOG
        cCalcHOG.restype=ctypes.POINTER(ctypes.c_float)
        
        _cellRow=ctypes.c_int(self.cellRow)
        _cellCol=ctypes.c_int(self.cellCol)
        _blockWidth=ctypes.c_int(self.blockWidth)
        _blockHeight=ctypes.c_int(self.blockHeight)
        _histgramDimension=ctypes.c_int(self.histgramDimension)
        desc=cCalcHOG(ctypes.byref(imgPix),_cellRow,_cellCol,_blockWidth,_blockHeight,_histgramDimension)
        
        _dim=self.histgramDimension*self.blockWidth*self.blockHeight*(self.cellRow-self.blockHeight)*(self.cellCol-self.blockWidth)
        self._v=[desc[i] for i in xrange(_dim)]
        del desc
        
class GIST(FeatureVector):
    numBlock=4
    orientationPerScale=[8,8,4]
    class _ColorImageT(ctypes.Structure):
        _fields_=[
                ("width",ctypes.c_int)
                ,("height",ctypes.c_int)
                ,("c1",ctypes.POINTER(ctypes.c_float))
                ,("c2",ctypes.POINTER(ctypes.c_float))
                ,("c3",ctypes.POINTER(ctypes.c_float))
                ]
    def _createColorImageTFromPIL(self,img):
        width,height=img.size
        
        cFloats=ctypes.c_float*(width*height) 
        c1=cFloats()
        c2=cFloats()
        c3=cFloats()
        
        pix=img.load()
        for y in xrange(height):
                for x in xrange(width):
                        i=y*width+x
                        c1[i],c2[i],c3[i]=pix[x,y]
                        
        return self._ColorImageT(width,height,c1,c2,c3)
    
    def _calc(self):
        nblocks=ctypes.c_int(self.numBlock)
        n_scale=ctypes.c_int(len(self.orientationPerScale))
        orientations_per_scale=(ctypes.c_int*len(self.orientationPerScale))(*self.orientationPerScale)
        
        cColorGistScaletab=libGIST.color_gist_scaletab
        cColorGistScaletab.restype=ctypes.POINTER(ctypes.c_float)
        
        desc=cColorGistScaletab(ctypes.byref(self._createColorImageTFromPIL(self.image)),nblocks,n_scale,orientations_per_scale)
        
        descsize=sum((self.numBlock*self.numBlock*ori for ori in  self.orientationPerScale))*3        
        self._v=[desc[i] for i in xrange(descsize)]
        del desc

class SURF:
    def __init__(self,image,maxsize=0):
        if maxsize==0:
            self.image=image
        else:
            self.image=SimpleFilter.Zoom(image,maxsize)

    def GetKeypoints(self):
        '''
        returns SURF Keypoints
        型は勝手にnumpy.matrixにした。
        
        numpy.matrix([<kp1>,<kp2>,<kp3>, ... ])
        <kp_> := [positionX,positionY,directionX,directionY,laplacianX,laplacianY]
        '''
        if not hasattr(self,"_kp"):
            self._calc()
        return self._kp

    def GetDescriptors(self):
        '''
        returns SURF Descriptors
        型は勝手にnumpy.matrixにした。
        
        numpy.matrix([<d1>,<d2>,<d3>, ... ])
        <d_>はそれぞれ128次元ベクトル
        '''
        if not hasattr(self,"_v"):
            self._calc()
        return self._v

    def GetVector(self,dammy=False):
        if dammy:
            return self.GetKeypoints(),self.GetDescriptors()
        raise NotImplementedError("SURFは1画像に対して多くの特徴点を抽出するアルゴリズムです。\n Call this function with 1 parameter, GetVector(True), or use GetKeypoints() or GetDescriptors() instead this method.")
    
    def _calc(self):
        img=self.image
        if img.mode!="L":
            img=img.convert("L")
        
        width,height=img.size
        _img=adaptors.PIL2Ipl(img)
        _keypoints,_descriptors=cv.cvExtractSURF(_img,None,cv.cvSURFParams(500,True))
        
        self._kp=adaptors.Ipl2NumPy(_keypoints).tolist()
        self._v=adaptors.Ipl2NumPy(_descriptors).tolist()

        del _keypoints,_descriptors
        del _img,_param
        del img
def CalcSURF2JSONfile(filename):
    os.system(os.path.abspath(os.path.dirname(__file__))+"/calcSURF.o %s > %s.surf.json"%(filename,filename))

            